﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using CodeCamp.Controllers;
using MIC = Microsoft.IdentityModel.Claims;
using SSP = System.Security.Principal;
using SWS = System.Web.Security;
using CCM = CodeCamp.Model;

namespace CodeCamp.Infrastructure
{

    public static class ClaimsPrincipalHelper
    {

        public static void AddClaim(this MIC.ClaimsIdentity claimsIdentity, MIC.Claim claim)
        {
            claimsIdentity.Claims.Add(claim);
        }

        public static void RemoveClaim(this MIC.ClaimsIdentity claimsIdentity, MIC.Claim claim)
        {
            claimsIdentity.Claims.Remove(claim);
        }
        
        public static IEnumerable<MIC.Claim> FindAll(this MIC.ClaimsPrincipal claimsPrincipal, string claimType)
        {
            List<MIC.Claim> claimList = new List<MIC.Claim>();
            MIC.ClaimsIdentity claimsIdent = claimsPrincipal.Identity as MIC.ClaimsIdentity;
            if ((claimsIdent != null) && !string.IsNullOrWhiteSpace(claimType))
            {
                foreach (var claim in claimsIdent.Claims)
                {
                    if (claim.ClaimType == claimType)
                        claimList.Add(claim);
                }
            }
            return claimList;
        }

        public static MIC.Claim FindFirst(this MIC.ClaimsPrincipal claimsPrincipal, string claimType)
        {
            List<MIC.Claim> claimList = new List<MIC.Claim>();
            MIC.ClaimsIdentity claimsIdent = claimsPrincipal.Identity as MIC.ClaimsIdentity;
            if ((claimsIdent != null) && !string.IsNullOrWhiteSpace(claimType))
            {
                foreach (var claim in claimsIdent.Claims)
                {
                    if (claim.ClaimType == claimType)
                        return claim;
                }
            }
            return null;
        }

        public static MIC.Claim FindFirst(this MIC.ClaimsPrincipal claimsPrincipal, Predicate<MIC.Claim> claimPredicate)
        {
            List<MIC.Claim> claimList = new List<MIC.Claim>();
            MIC.ClaimsIdentity claimsIdent = claimsPrincipal.Identity as MIC.ClaimsIdentity;
            if ((claimsIdent != null) && (claimPredicate != null))
            {
                foreach (var claim in claimsIdent.Claims)
                {
                    bool claimMatch = claimPredicate(claim);
                    if (claimMatch)
                        return claim;
                }
            }
            return null;
        }

    }

    public class LocalClaimsManager : MIC.ClaimsAuthenticationManager
    {
        private static class LocalClaimValues
        {
            internal const string CodeCamp = "CodeCamp";
            internal const string LOCAL_AUTHORITY = "LOCAL AUTHORITY";
            internal const string LocalIssuer = "CCLOCAL";
            internal const string username = "username";
            internal const string identityprovider_2010 = "http://schemas.microsoft.com/accesscontrolservice/2010/07/claims/identityprovider";
            internal const int localClaimExpirationMinutes = 15;
        }

        internal string EventShortName { get; set; }

        private static bool MatchLocalExpiration(MIC.Claim claimToMatch)
        {
            bool foundMatch = false;

            if ((claimToMatch != null) && (claimToMatch.ClaimType == MIC.ClaimTypes.Expiration))
            {
                foundMatch = true;
            }

            return foundMatch;
        }

        public override MIC.IClaimsPrincipal Authenticate(string resourceName, MIC.IClaimsPrincipal iincomingPrincipal)
        {
            MIC.ClaimsPrincipal incomingPrincipal  = iincomingPrincipal as MIC.ClaimsPrincipal;
            if ((incomingPrincipal != null) && incomingPrincipal.Identity.IsAuthenticated)
            {
                MIC.ClaimsIdentity claimsIdentity = incomingPrincipal.Identity as MIC.ClaimsIdentity;

                Predicate<MIC.Claim> claimPredicate = new Predicate<MIC.Claim>(MatchLocalExpiration);

                bool localExpirationClaimAdded = false;
                bool localClaimsValid = false;
                bool localExpirationClaimFound = false;
                MIC.Claim localExpirationClaim = incomingPrincipal.FindFirst(claimPredicate);
                if ((localExpirationClaim == null) && (claimsIdentity != null))
                {
                    DateTime expirationTime = DateTime.UtcNow.AddMinutes(LocalClaimValues.localClaimExpirationMinutes);
                    localExpirationClaim = new MIC.Claim(MIC.ClaimTypes.Expiration, expirationTime.ToString("u"), "string", LocalClaimValues.LocalIssuer);
                    claimsIdentity.AddClaim(localExpirationClaim);
                    localExpirationClaimAdded = true;
                }
                else
                {
                    localExpirationClaimFound = true;
                    DateTime expirationTime;
                    if (!string.IsNullOrWhiteSpace(localExpirationClaim.Value) && DateTime.TryParse(localExpirationClaim.Value, out expirationTime))
                    {
                        DateTime expirationTimeUTC = expirationTime.ToUniversalTime();
                        DateTime utcNow = DateTime.UtcNow;
                        if (utcNow < expirationTimeUTC)
                            localClaimsValid = true;
                    }
                }
                if (!localClaimsValid)
                {
                    if (localExpirationClaimFound)
                    {
                        List<MIC.Claim> claimsToRemove = new List<MIC.Claim>();
                        foreach (MIC.Claim claim in claimsIdentity.Claims)
                        {
                            if ((claim.Issuer == LocalClaimValues.LocalIssuer) || (claim.Issuer == LocalClaimValues.LOCAL_AUTHORITY))
                            {
                                claimsToRemove.Add(claim);
                            }
                        }
                        foreach (MIC.Claim claim in claimsToRemove)
                        {
                            claimsIdentity.RemoveClaim(claim);
                        }
                    }

#if USE_MEMBERSHIP
                    MIC.Claim nameClaim;
                    GetRequiredIdentityClaim(incomingPrincipal, out nameClaim);

                    if (nameClaim != null)
                    {
                        using (CodeCampDataRepository ccdr = new CodeCampDataRepository())
                        {
                            CCM.SiteUser siteUser = ccdr.GetSiteUser(LocalClaimValues.CodeCamp, LocalClaimValues.username, nameClaim.Value, true);
                            if (siteUser != null)
                            {
                                List<string> siteRoles = ccdr.GetSiteUserSiteRoles(siteUser.Id);
                                foreach (string roleName in siteRoles)
                                {
                                    claimsIdentity.AddClaim(new MIC.Claim(MIC.ClaimTypes.Role, roleName));
                                }
                            }
                        }
                    }
#else
                    MIC.Claim identityProviderClaim, nameIdentifierClaim;
                    GetRequiredIdentityClaims(incomingPrincipal, out identityProviderClaim, out nameIdentifierClaim);

                    if ((identityProviderClaim != null) && (nameIdentifierClaim != null))
                    {
                        using (CodeCampDataRepository ccdr = new CodeCampDataRepository())
                        {
                            CCM.SiteUser siteUser = ccdr.GetSiteUser(identityProviderClaim.Value, nameIdentifierClaim.ClaimType, nameIdentifierClaim.Value, true);
                            if (siteUser != null)
                            {
                                List<string> siteRoles = ccdr.GetSiteUserSiteRoles(siteUser.Id);
                                foreach (string roleName in siteRoles)
                                {
                                    claimsIdentity.AddClaim(new MIC.Claim(MIC.ClaimTypes.Role, roleName));
                                }
                            }
                        }
                    }
#endif
                    if (!localExpirationClaimAdded)
                    {
                        DateTime expirationTime = DateTime.UtcNow.AddMinutes(LocalClaimValues.localClaimExpirationMinutes);
                        localExpirationClaim = new MIC.Claim(MIC.ClaimTypes.Expiration, expirationTime.ToString("u"), "string", LocalClaimValues.LocalIssuer);
                        claimsIdentity.AddClaim(localExpirationClaim);
                    }
                }
            }
            return incomingPrincipal;
        }

#if !USE_MEMBERSHIP
        internal static void RemoveLocalClaims(MIC.IClaimsPrincipal iclaimsPrincipal)
        {
            MIC.ClaimsPrincipal claimsPrincipal  = iclaimsPrincipal as MIC.ClaimsPrincipal;
            if ((claimsPrincipal != null) && claimsPrincipal.Identity.IsAuthenticated)
            {
                MIC.ClaimsIdentity claimsIdentity = claimsPrincipal.Identity as MIC.ClaimsIdentity;
                if (claimsIdentity != null)
                {
                    Predicate<MIC.Claim> claimPredicate = new Predicate<MIC.Claim>(MatchLocalExpiration);
                    MIC.Claim localExpirationClaim = claimsPrincipal.FindFirst(claimPredicate);
                    if (localExpirationClaim != null)
                    {
                        List<MIC.Claim> claimsToRemove = new List<MIC.Claim>();
                        foreach (MIC.Claim claim in claimsIdentity.Claims)
                        {
                            if (claim.Issuer == LocalClaimValues.LocalIssuer)
                            {
                                claimsToRemove.Add(claim);
                            }
                        }
                        foreach (MIC.Claim claim in claimsToRemove)
                        {
                            claimsIdentity.RemoveClaim(claim);
                        }
                    }
                }
            }
        }
#endif


#if USE_MEMBERSHIP
        private void GetRequiredIdentityClaim(MIC.ClaimsPrincipal claimsPrincipal, out MIC.Claim nameClaim)
        {
            nameClaim = claimsPrincipal.FindFirst(MIC.ClaimTypes.Name);
        }
#else

        private void GetRequiredIdentityClaims(MIC.ClaimsPrincipal claimsPrincipal, out MIC.Claim identityProviderClaim, out MIC.Claim nameIdentifierClaim)
        {
            identityProviderClaim = null;
            nameIdentifierClaim = null;
            IEnumerable<MIC.Claim> claims = claimsPrincipal.FindAll(LocalClaimValues.identityprovider_2010);
            int claimCount = claims.Count();
            identityProviderClaim = claims.FirstOrDefault();
            claims = claimsPrincipal.FindAll(MIC.ClaimTypes.NameIdentifier);
            claimCount = claims.Count();
            nameIdentifierClaim = claims.FirstOrDefault();
        }
#endif

    }
}